﻿#region License statement
// NJamb, a specification and delta-specification DSL
// Copyright (c) 2010-2011, Mark Knell
// Published under the MIT License; all other rights reserved
#endregion

#region using...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using NJamb.Parser.SpecificationBuilders;
using NJamb.Parser.Subjects.Enumerable;
using NJamb.Parser.Subjects.Singular;
using NJamb.Parser.Subjects.Void;
using NJamb.Specifications.Delta;
using NJamb.Specifications.Void;
#endregion

namespace NJamb.Parser.SubjectBuilders
{
	public static class SubjectBuilderExtensions
	{
		public static ISingularSubject<TSubject> That<THost, TSubject>(this ISubjectBuilder<THost> subjectBuilder,
			Expression<Func<THost, TSubject>> subjectExpression) where THost : class
		{
			Expression<Func<THost>> expression = subjectBuilder.Xray.Expression;
			Func<TSubject> getter = () => MakeGetter(expression, subjectExpression);
			var lazyDescription = new Lazy<string>(() => subjectBuilder.Xray.Describe(subjectExpression));
			return new SingularSubject<TSubject>(getter, lazyDescription);
		}

		public static ICanThrow<IVoidSpecification<THost>> That<THost>(this ISubjectBuilder<THost> subjectBuilder,
			Expression<Action<THost>> action) where THost : class
		{
			return new VoidSubject<THost>(subjectBuilder, action);
		}

		public static IRaisePropertyChangedDeltaSpecification<THost> ThatItWillRaiseAPropertyChangedEventFor<THost, TSubject>(
			this ISubjectBuilder<THost> subjectBuilder, Expression<Func<THost, TSubject>> propertyExpression)
			where THost : class, INotifyPropertyChanged
		{
			return RaisePropertyChangedDeltaSpecification<THost>.Make(subjectBuilder, propertyExpression);
		}

		public static IEnumerableSubject<ICollection<TItem>, TItem> ThatTheCollection<THost, TItem>(
			this ISubjectBuilder<THost> subjectBuilder, Expression<Func<THost, ICollection<TItem>>> enumerableExpression)
			where THost : class
		{
			Expression<Func<THost>> expression = subjectBuilder.Xray.Expression;
			Func<ICollection<TItem>> getter = () => MakeGetter(expression, enumerableExpression);
			var lazyDescription = new Lazy<string>(() => subjectBuilder.Xray.Describe(enumerableExpression));
			return new EnumerableSubject<ICollection<TItem>, TItem>(getter, lazyDescription);
		}

		public static IEnumerableSubject<IEnumerable<TItem>, TItem> ThatTheSequence<THost, TItem>(
			this ISubjectBuilder<THost> subjectBuilder, Expression<Func<THost, IEnumerable<TItem>>> enumerableExpression)
			where THost : class
		{
			Expression<Func<THost>> expression = subjectBuilder.Xray.Expression;
			Func<IEnumerable<TItem>> getter = () => MakeGetter(expression, enumerableExpression);
			var lazyDescription = new Lazy<string>(() => subjectBuilder.Xray.Describe(enumerableExpression));
			return new EnumerableSubject<IEnumerable<TItem>, TItem>(getter, lazyDescription);
		}

		private static TTarget MakeGetter<THost, TTarget>(Expression<Func<THost>> expression,
			Expression<Func<THost, TTarget>> subjectExpression)
		{
			Func<THost> hostGetter = expression.Compile();
			THost host = hostGetter.Invoke();
			Func<THost, TTarget> subjectExtractor = subjectExpression.Compile();
			TTarget subject = subjectExtractor.Invoke(host);
			return subject;
		}
	}

}
